﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ASoft.Msg;
using ASoft.Repository;

namespace ASoft.Service
{
    public class MsgService
    {
        Dictionary<int, DateTime> roomTimes;
        DateTime weatherStationTime;
        WeatherStationRepository weatherStationRepository = new WeatherStationRepository();
        RoomInfoRepository roomInfoRepository = new RoomInfoRepository();
        Dictionary<int, RoomReturnSearchMsg> lastRoomDatas = new Dictionary<int, RoomReturnSearchMsg>();

        public MsgService(SerialPortService serialPortService)
        {
            SerialPortService = serialPortService;
            InitMsgRelation();
            InitHistoryTime();
            InitLastRoomDatas();
        }

        private void InitLastRoomDatas()
        {
            for (int i = 0; i <= 256; i++)
            {
                lastRoomDatas[i] = null;
            }
        }

        private void InitHistoryTime()
        {
            weatherStationTime = DateTime.Now;
            roomTimes = new Dictionary<int, DateTime>();
            for (int i = 0; i < 255; i++)
            {
                roomTimes[i] = weatherStationTime;
            }
        }


        public SerialPortService SerialPortService { get; private set; }

        Dictionary<Type, Type> MsgRelation { get; set; }

        private void InitMsgRelation()
        {
            MsgRelation = new Dictionary<Type, Type>();
            MsgRelation[typeof(WeatherStationSearchMsg)] = typeof(WeatherStationReturnSearchMsg);
            MsgRelation[typeof(WeatherStationSetMsg)] = typeof(WeatherStationReturnSetMsg);
            MsgRelation[typeof(RoomReadMsg)] = typeof(RoomReturnReadMsg);
            MsgRelation[typeof(RoomCoerceStartMsg)] = null;
            MsgRelation[typeof(RoomSearchMsg)] = typeof(RoomReturnSearchMsg);
            MsgRelation[typeof(RoomSetMsg)] = typeof(RoomReturnSetMsg);
            MsgRelation[typeof(RoomSetActionMsg)] = typeof(RoomReturnSetActionMsg);
            MsgRelation[typeof(PhecSearch)] = typeof(PhecReturnSearch);
        }

        public event MsgReceivedEventHandler MsgReceived;

        public void OnMsgReceived(object sender, MsgReceiveEventArgs e)
        {
            if (MsgReceived != null)
            {
                MsgReceived(sender, e);
            }
        }

        static object lockKey = new object();

        public MsgBase TransSendMsg(MsgBase msg)
        {
            lock (lockKey)
            {
                var isOpen = SerialPortService.IsOpen;
                MsgBase result = null;
                try
                {
                    if (!isOpen)
                    {
                        SerialPortService.OpenPort();
                    }
                    result = SendMsg(msg);

                }
                finally
                {
                    if (!isOpen)
                    {
                        SerialPortService.ClosePort();
                    }
                }
                return result;
            }
        }

        public MsgBase SendMsg(MsgBase msg)
        {
            if (!MsgRelation.ContainsKey(msg.GetType()))
            {
                throw new Exception("当前消息不可发送");
            }
            var sendData = msg.GetData();
            var receiveType = MsgRelation[msg.GetType()];
            byte[] receiveData;
            if (receiveType == null)
            {
                receiveData = null;
            }
            else
            {
                receiveData = MsgFactory.GetEmptyMsgData(receiveType);
                var p = receiveType.GetProperty("Id", System.Reflection.BindingFlags.Public
                     | System.Reflection.BindingFlags.Instance
                     | System.Reflection.BindingFlags.IgnoreCase);
                if (p != null && p.CanWrite)
                {
                    //所有非温室消息Id必须设置为只读
                    receiveData[2] = sendData[2];
                }
            }

            WriteDbLog(msg);

            var receiveState = SerialPortService.SendCommand(msg.GetData(), ref receiveData, 40);

            if (receiveData == null)
            {
                return null;
            }

            if (!receiveState)
            {
                //如果接收数据错误，就将原数据返回。
                OnMsgReceived(this, new MsgReceiveEventArgs(false, msg));
                return null;
            }

            var result = MsgFactory.GetMsg(receiveData);

            WriteDbLog(result);
            偏移处理(result);
            WriteHistory(result);

            OnMsgReceived(this, new MsgReceiveEventArgs(true, result));



            return result;

        }

        private void 偏移处理(MsgBase result)
        {
            if (result is WeatherStationReturnSearchMsg)
            {
                var obj = result as WeatherStationReturnSearchMsg;
                obj.风速 += RC.Current.RootData.偏移风速;
                obj.风向 += (byte)RC.Current.RootData.偏移风向;
                obj.光照 += (byte)RC.Current.RootData.偏移光照;
                obj.室外温度 += (byte)RC.Current.RootData.偏移室外温度;
                obj.雨雪 += (byte)RC.Current.RootData.偏移雨雪;
            }
            else if (result is RoomReturnSearchMsg)
            {
                var obj = result as RoomReturnSearchMsg;
                var py = RC.Current.RootData.温室集合.First(p => p.Id == obj.Id);
                obj.P1拉幕位置 += (byte)py.偏移P1拉幕位置;
                obj.P2拉幕位置 += (byte)py.偏移P2拉幕位置;

                obj.灌溉加湿EX += (byte)py.偏移灌溉加湿;
                obj.光照 += (byte)py.偏移光照;
                obj.P1通风窗位置 += (byte)py.偏移P1通风窗位置;
                obj.P2通风窗位置 += (byte)py.偏移P2通风窗位置;
                obj.湿度1 += (byte)py.偏移湿度1;
                obj.湿度2 += (byte)py.偏移湿度2;
                obj.室内温度1 += py.偏移室内温度1;
                obj.室内温度2 += py.偏移室内温度2;
                obj.土壤湿度 += (byte)py.偏移土壤湿度;
                obj.室内CO2 += (byte)py.偏移二氧化碳;
            }
        }

        void WriteDbLog(MsgBase msg)
        {
            if (msg == null)
            {
                return;
            }

            var att = msg.GetType().GetCustomAttributes(typeof(RecordTypeAttributeAttribute), false)
                .FirstOrDefault() as RecordTypeAttributeAttribute;
            if (att == null)
                return;
            var recordKey = 0;
            var idProperty = msg.GetType().GetProperty("Id");
            if (idProperty != null)
            {
                recordKey = (byte)idProperty.GetValue(msg, null);
            }
            var entity = new ASoftDataEntity()
            {
                RecordType = att.RecordType,
                RecordTime = DateTime.Now,
                RecordKey = recordKey,
                RecordData = AppUtility.GetMsgRecordData(msg)
            };
            ASoftDataRepository repository = new ASoftDataRepository();
            repository.Insert(entity);
        }


        private void WriteHistory(MsgBase result)
        {
            var weatherStationMsg = result as WeatherStationReturnSearchMsg;
            var roomMsg = result as RoomReturnSearchMsg;
            var phecReturn = result as PhecReturnSearch;

            var minute = RC.Current.RootData.数据记录间隔分钟数;
            if (minute == 0)
            {
                minute = 10;
            }

            if (weatherStationMsg != null && weatherStationTime < DateTime.Now)
            {
                weatherStationTime = weatherStationTime.AddMinutes(minute);
                var entity = new WeatherStationInfoEntity();
                entity.时间 = DateTime.Now;
                SetValue(weatherStationMsg, entity);
                weatherStationRepository.Insert(entity);
            }
            if (roomMsg != null)
            {
                if (roomTimes[roomMsg.Id] > DateTime.Now)
                {
                    return;
                }

                if (!发生漂移(roomMsg))
                {
                    roomTimes[roomMsg.Id] = roomTimes[roomMsg.Id].AddMinutes(minute);
                    var entity = new RoomInfoEntity();
                    entity.时间 = DateTime.Now;
                    SetValue(roomMsg, entity);
                    roomInfoRepository.Insert(entity);
                }
            }

            if (phecReturn != null)
            {
                if (roomTimes[phecReturn.Id] > DateTime.Now)
                {
                    return;
                }
                roomTimes[phecReturn.Id] = roomTimes[phecReturn.Id].AddMinutes(minute);
                var entity = new RoomInfoEntity();
                entity.时间 = DateTime.Now;
                entity.Id = phecReturn.Id;
                entity.Ph = phecReturn.Ph;
                entity.Ec = phecReturn.Ec;
                entity.IsPhec = true;
                roomInfoRepository.Insert(entity);
            }
        }

        private bool 发生漂移(RoomReturnSearchMsg roomMsg)
        {
            var roomData = RC.Current.RootData.温室集合.Single(p => p.Id == roomMsg.Id);

            if (!roomData.启用飘移检测)
            {
                return false;
            }

            var lastRoomData = lastRoomDatas[roomMsg.Id];
            if (lastRoomData == null)
            {
                lastRoomDatas[roomMsg.Id] = roomMsg;
                return false;
            }

            if (
                (roomData.飘移湿度1 == 0 ||
                roomData.飘移湿度1 != 0
                && (int)roomMsg.湿度1 <= ((int)lastRoomData.湿度1 + roomData.飘移湿度1)
                && (int)roomMsg.湿度1 >= ((int)lastRoomData.湿度1 - roomData.飘移湿度1))

                && (roomData.飘移湿度2 == 0 ||
                roomData.飘移湿度2 != 0
                && (int)roomMsg.湿度2 <= ((int)lastRoomData.湿度2 + roomData.飘移湿度2)
                && (int)roomMsg.湿度2 >= ((int)lastRoomData.湿度2 - roomData.飘移湿度2))

                && (roomData.飘移室内温度1 == 0 ||
                roomData.飘移室内温度1 != 0
                && (int)roomMsg.室内温度1 <= ((int)lastRoomData.室内温度1 + roomData.飘移室内温度1)
                && (int)roomMsg.室内温度1 >= ((int)lastRoomData.室内温度1 - roomData.飘移室内温度1))

                && (roomData.飘移室内温度2 == 0 ||
                roomData.飘移室内温度2 != 0
                && (int)roomMsg.室内温度2 <= ((int)lastRoomData.室内温度2 + roomData.飘移室内温度2)
                && (int)roomMsg.室内温度2 >= ((int)lastRoomData.室内温度2 - roomData.飘移室内温度2))

                && (roomData.飘移光照 == 0 ||
                roomData.飘移光照 != 0
                && (int)roomMsg.室内光照 <= ((int)lastRoomData.室内光照 + roomData.飘移光照)
                && (int)roomMsg.室内光照 >= ((int)lastRoomData.室内光照 - roomData.飘移光照))

                && (roomData.飘移二氧化碳 == 0 ||
                roomData.飘移二氧化碳 != 0
                && (int)roomMsg.室内CO2 <= ((int)lastRoomData.室内CO2 + roomData.飘移二氧化碳)
                && (int)roomMsg.室内CO2 >= ((int)lastRoomData.室内CO2 - roomData.飘移二氧化碳))

                && (roomData.飘移土壤湿度 == 0 ||
                roomData.飘移土壤湿度 != 0
                && (int)roomMsg.土壤湿度 <= ((int)lastRoomData.土壤湿度 + roomData.飘移土壤湿度)
                && (int)roomMsg.土壤湿度 >= ((int)lastRoomData.土壤湿度 - roomData.飘移土壤湿度))

                )
            {
                lastRoomDatas[roomMsg.Id] = roomMsg;
                return false;
            }
            return true;
        }

        void SetValue(object source, object target)
        {
            var ps = from p1 in source.GetType().GetProperties()
                     join p2 in target.GetType().GetProperties()
                     on p1.Name equals p2.Name
                     select new { PSource = p1, PTarget = p2 };
            foreach (var item in ps)
            {
                item.PTarget.SetValue(target, item.PSource.GetValue(source, null), null);
            }
        }
    }

    public class MsgReceiveEventArgs : EventArgs
    {
        public MsgReceiveEventArgs(bool isValid, MsgBase msg)
        {
            IsValid = isValid;
            Msg = msg;
        }

        public bool IsValid { get; private set; }

        public MsgBase Msg { get; private set; }
    }

    public delegate void MsgReceivedEventHandler(object sender, MsgReceiveEventArgs e);
}
